{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Quantum dynamics resource estimation\n",
    "\n",
    "In this Q# notebook we demonstrate resource estimation for quantum dynamics,\n",
    "specifically the simulation of an Ising model Hamiltonian on an $N \\times N$ 2D\n",
    "lattice using a *fourth-order Trotter Suzuki product formula* assuming a 2D\n",
    "qubit architecture with nearest-neighbor connectivity.\n",
    "\n",
    "First, we connect to the Azure quantum service and load the necessary packages."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from azure.quantum import Workspace\n",
    "from azure.quantum.target.microsoft import MicrosoftEstimator, QubitParams, QECScheme\n",
    "import qsharp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "workspace = Workspace (\n",
    "    resource_id = \"\",\n",
    "    location = \"\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "qsharp.packages.add(\"Microsoft.Quantum.Numerics\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Background: 2D Ising model\n",
    "\n",
    "The Ising model is a mathematical model of ferromagnetism in a lattice (in our case a 2D square lattice) with two kinds of terms in the Hamiltonian: (i) an interaction term between adjacent sites and (ii) an external magnetic field acting at each site. For our purposes, we consider a simplified version of the model where the interaction terms have the same strength and the external field strength is the same at each site.\n",
    "Formally, the Ising model Hamiltonian on an $N \\times N$ lattice we consider is formulated as:\n",
    "\n",
    "$$\n",
    "H = \\underbrace{-J \\sum_{i, j} Z_i Z_j}_{B} + \\underbrace{g \\sum_j X_j}_{A}\n",
    "$$\n",
    "where $J$ is the interaction strength, $g$ is external field strength.\n",
    "\n",
    "The time evolution $e^{-iHt}$ for the Hamiltonian is simulated with the fourth-order product formula so that any errors in simulation are sufficiently small. Essentially, this is done by simulating the evolution for small slices of time $\\Delta$ and repeating this for `nSteps` $= t/\\Delta$ to obtain the full time evolution. The Trotter-Suzuki formula for higher orders can be recursively defined using a *fractal decomposition* as discussed in Section 3 of [[Hatanao and Suziki's survey](https://link.springer.com/chapter/10.1007/11526216_2)]. Then the fourth order formula $U_4(\\Delta)$ can be constructed using the second-order one $U_2(\\Delta)$ as follows.\n",
    "$$\n",
    "\\begin{aligned}\n",
    "U_2(\\Delta) & = e^{-iA\\Delta/2} e^{-iB\\Delta} e^{-iA\\Delta/2}; \\\\\n",
    "U_4(\\Delta) & = U_2(p\\Delta)U_2(p\\Delta)U_2((1 - 4p)\\Delta)U_2(p\\Delta)U_2(p\\Delta); \\\\\n",
    "p & = (4 - 4^{1/3})^{-1}.\n",
    "\\end{aligned}\n",
    "$$\n",
    "\n",
    "For the rest of the notebook, we will present the code that computes the time evolution in a step by step fashion.\n",
    "\n",
    "## Implementation\n",
    "\n",
    "### Helper functions\n",
    "\n",
    "We will allocate all qubits in the 2D lattice in a one-dimensional array.  The function `GetQubitIndex` converts a qubit identified on a 2D lattice by `(row, col)` to an index in that array. We assume a snake-like order on the 2D lattice i.e., the numbering goes left-to-right on even rows and right-to-left on odd rows."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%qsharp\n",
    "function GetQubitIndex(row : Int, col : Int, n : Int) : Int {\n",
    "    return row % 2 == 0             // if row is even,\n",
    "        ? col + n * row             // move from left to right,\n",
    "        | (n - 1 - col) + n * row;  // otherwise from right to left.\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that expanding $U_4(\\Delta)$ to express it in terms of $A, B$ gives:\n",
    "$$\n",
    "U_4(\\Delta) = e^{-iAp\\Delta/2} e^{-iBp\\Delta} e^{-iAp\\Delta} e^{-iBp\\Delta} e^{-iA(1 - 3p)\\Delta/2} e^{-iB(1-4p)\\Delta} e^{-iA(1 - 3p)\\Delta/2} e^{-iBp\\Delta} e^{-iAp\\Delta} e^{-iBp\\Delta} e^{-iAp\\Delta/2}\n",
    "$$\n",
    "\n",
    "The above equation with $11$ exponential terms works for one time step. For `nSteps` $> 1$ time steps, some adjacent terms can be merged to give $10t+1$ exponential terms for $e^{-iHt}$.\n",
    "\n",
    "The function below creates two sequences `seqA` and `seqB` corresponding to the constant factors that will be applied with $A$ and $B$, respectively, in the exponential sequence of the above formula."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%qsharp\n",
    "function SetSequences(len : Int, p : Double, dt : Double, J : Double, g : Double) : (Double[], Double[]) {\n",
    "    // create two arrays of size `len`\n",
    "    mutable seqA = [0.0, size=len];\n",
    "    mutable seqB = [0.0, size=len];\n",
    "\n",
    "    // pre-compute values according to exponents\n",
    "    let values = [\n",
    "        -J * p * dt,\n",
    "        g * p * dt,\n",
    "        -J * p * dt,\n",
    "        g * p * dt,\n",
    "        -J * (1.0 - 3.0 * p) * dt / 2.0,\n",
    "        g * (1.0 - 4.0 * p) * dt,\n",
    "        -J * (1.0 - 3.0 * p) * dt / 2.0,\n",
    "        g * p * dt,\n",
    "        -J * p * dt,\n",
    "        g * p * dt\n",
    "    ];\n",
    "\n",
    "    // assign first and last value of `seqA`\n",
    "    set seqA w/= 0 <- -J * p * dt / 2.0;\n",
    "    set seqA w/= len - 1 <- -J * p * dt / 2.0;\n",
    "\n",
    "    // assign other values to `seqA` or `seqB`\n",
    "    // in an alternating way\n",
    "    for i in 1..len - 2 {\n",
    "        if i % 2 == 0 {\n",
    "            set seqA w/= i <- values[i % 10];\n",
    "        }\n",
    "        else {\n",
    "            set seqB w/= i <- values[i % 10];\n",
    "        }\n",
    "    }\n",
    "\n",
    "    return (seqA, seqB);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Quantum operations\n",
    "\n",
    "There are two kinds of Pauli exponentials needed for simulating the time evolution of an Ising Model:\n",
    "- The transverse field $e^{-iX\\theta}$ applied to each qubit for an angle $\\theta$;\n",
    "- $e^{-i (Z \\otimes Z)\\theta}$ applied to neighboring pairs of qubits in the lattice.\n",
    "\n",
    "The operation below applies $e^{-iX\\theta}$ on all qubits in the 2D lattice."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%qsharp\n",
    "operation ApplyAllX(qs : Qubit[], theta : Double) : Unit {\n",
    "    // This applies `Rx` with an angle of `2.0 * theta` to all qubits in `qs`\n",
    "    // using partial application\n",
    "    ApplyToEach(Rx(2.0 * theta, _), qs);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next operation below applies $e^{-i(Z \\otimes Z)\\theta}$ on overlapping pairs of neighboring qubits. We decompose this term into a single qubit $e^{-iZ\\theta}$ term (implemented as an `Rz` rotation) conjugated by `CNOT`s to entangle the neighboring qubits following Section 4.2 of [[Whitfield et al.](https://www.tandfonline.com/doi/abs/10.1080/00268976.2011.552441)].\n",
    "\n",
    "Observe that unlike the previous case, it is not possible to simultaneously apply all the rotations in one go. For example, while applying the rotation on qubits at $(0, 0)$ and $(0, 1)$, it is not possible to also apply the rotation on qubits $(0, 1)$ and $(0, 2)$. Instead, we try to apply as many rotations as possible. This is broken up as follows:\n",
    "- in the horizontal (resp. vertical) direction of the 2D lattice as chosen by `dir`,\n",
    "- consider pairs starting with an even (resp. odd) index as given by `grp`;\n",
    "- apply the exponential to all such pairs in the lattice."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%qsharp\n",
    "operation ApplyDoubleZ(n : Int, qs : Qubit[], theta : Double, dir : Bool, grp : Bool) : Unit {\n",
    "    let start = grp ? 0 | 1;    // Choose either odd or even indices based on group number\n",
    "\n",
    "    for i in 0..n - 1 {\n",
    "        for j in start..2..n - 2 {    // Iterate through even or odd `j`s based on `grp`\n",
    "            // rows and cols are interchanged depending on direction\n",
    "            let (row, col) = dir ? (i, j) | (j, i);\n",
    "\n",
    "            // Choose first qubit based on row and col\n",
    "            let ind1 = GetQubitIndex(row, col, n);\n",
    "            // Choose second qubit in column if direction is horizontal and next qubit in row if direction is vertical\n",
    "            let ind2 = dir ? GetQubitIndex(row, col + 1, n) | GetQubitIndex(row + 1, col, n);\n",
    "\n",
    "            within {\n",
    "                CNOT(qs[ind1], qs[ind2]);\n",
    "            } apply {\n",
    "                Rz(2.0 * theta, qs[ind2]);\n",
    "            }\n",
    "        }\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next operation puts everything together and calls the operations needed to\n",
    "simulate the Ising model Hamiltonian using a fourth order product formula.\n",
    "Observe that the `ApplyDoubleZ` operation is called four times for different\n",
    "choices of direction and starting index to ensure all possible pairs of qubits\n",
    "are appropriately considered.\n",
    "\n",
    "The various parameters taken in by the operation correspond to:\n",
    "\n",
    "- `J`, `g`: parameters by which the Hamiltonian terms are scaled.\n",
    "- `N`: size of the square lattice.\n",
    "- `totTime`: the number of Trotter steps.\n",
    "- `dt` : the step size for the simulation, sometimes denoted as $\\Delta$.\n",
    "- `eps`: the precision for arbitrary rotations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%qsharp\n",
    "open Microsoft.Quantum.Math;\n",
    "\n",
    "operation IsingModel2DSim(N : Int, J : Double, g : Double, totTime : Double, dt : Double, eps : Double) : Unit {\n",
    "    use qs = Qubit[N * N];\n",
    "    let len = Length(qs);\n",
    "\n",
    "    let p = 1.0 / (4.0 - PowD(4.0, 1.0 / 3.0));\n",
    "    let t = Ceiling(totTime / dt);\n",
    "\n",
    "    let seqLen = 10 * t + 1;\n",
    "\n",
    "    let (seqA, seqB) = SetSequences(seqLen, p, dt, J, g);\n",
    "\n",
    "    for i in 0..seqLen - 1 {\n",
    "        // for even indexes\n",
    "        if i % 2 == 0 {\n",
    "            ApplyAllX(qs, seqA[i]);\n",
    "        } else {\n",
    "            // iterate through all possible combinations for `dir` and `grp`.\n",
    "            for (dir, grp) in [(true, true), (true, false), (false, true), (false, false)] {\n",
    "                ApplyDoubleZ(N, qs, seqB[i], dir, grp);\n",
    "            }\n",
    "        }\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Running the experiment\n",
    "\n",
    "Next, we are estimating the physical resource estimates to simulate the Ising\n",
    "model Hamiltonian for a $10 \\times 10$ lattice with $J = g = 1.0$, total time\n",
    "$20$, step size $0.25$, and `eps` ${}=0.001$. As configurations for the\n",
    "experiment we use all six pre-defined qubit parameters.  As pre-defined QEC\n",
    "scheme we are using `surface_code` with gate-based qubit parameters (default),\n",
    "and `floquet_code` with Majorana based qubit parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "estimator = MicrosoftEstimator(workspace)\n",
    "\n",
    "labels = [\"Gate-based µs, 10⁻³\", \"Gate-based µs, 10⁻⁴\", \"Gate-based ns, 10⁻³\", \"Gate-based ns, 10⁻⁴\", \"Majorana ns, 10⁻⁴\", \"Majorana ns, 10⁻⁶\"]\n",
    "\n",
    "params = estimator.make_params(num_items=6)\n",
    "params.arguments[\"N\"] = 10\n",
    "params.arguments[\"J\"] = 1.0\n",
    "params.arguments[\"g\"] = 1.0\n",
    "params.arguments[\"totTime\"] = 20.0\n",
    "params.arguments[\"dt\"] = 0.25\n",
    "params.arguments[\"eps\"] = 0.001\n",
    "params.items[0].qubit_params.name = QubitParams.GATE_US_E3\n",
    "params.items[1].qubit_params.name = QubitParams.GATE_US_E4\n",
    "params.items[2].qubit_params.name = QubitParams.GATE_NS_E3\n",
    "params.items[3].qubit_params.name = QubitParams.GATE_NS_E4\n",
    "params.items[4].qubit_params.name = QubitParams.MAJ_NS_E4\n",
    "params.items[4].qec_scheme.name = QECScheme.FLOQUET_CODE\n",
    "params.items[5].qubit_params.name = QubitParams.MAJ_NS_E6\n",
    "params.items[5].qec_scheme.name = QECScheme.FLOQUET_CODE"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We are submitting a resource estimation job with all target parameter configurations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "job = estimator.submit(IsingModel2DSim, input_params=params)\n",
    "results = job.get_results()"
   ]
  },
  {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
      "## Visualizing and understanding the results\n",
      "\n",
      "### Result summary table"
    ]
  },
  {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
      "results.summary_data_frame(labels=labels)"
    ]
  },
  {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
      "### Space chart\n"
    ]
  },
  {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
      "The distribution of physical qubits used for the execution of the algorithm instructions and the supporting T factories can provide us valuable information to guide us in applying space and time optimizations. We can visualize this distribution for each set of qubit parameters to understand the differences in physical qubit distribution for each configuration.\n",
      "\n",
      "To show the space chart for a configuration from our experiment, use the following syntax:\n",
      "\n",
      "```\n",
      "    # Use the index of the desired configuration you want to visualize to get the results for that configuration.\n",
      "    \n",
      "    results[<item index>].diagram.space\n",
      "\n",
      "    # For example, this command will produce a chart showing the distribution of physical qubits for the Gate-based µs, 10⁻³ configuration set.\n",
      "    results[0].diagram.space \n",
      "```\n",
      "\n",
      "Below, let's visualize the space diagrams for each configuration."
    ]
  },
  {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
      "> You must run the `results[<item index>].diagram.space` command in a separate code cell per label.\n",
      ">\n",
      "> You cannot visualize the time and space diagrams in the same cell.\n",
      ">\n",
      "> If you run an algorithm which only has one configured set of qubit parameters and one result set, specifying the label would not be necessary. You could simply run `result.diagram.time`"
    ]
  },
  {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
      "# \"Gate-based µs, 10⁻³\"\n",
      "results[0].diagram.space"
    ]
  },
  {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
      "# \"Gate-based µs, 10⁻⁴\"\n",
      "results[1].diagram.space"
    ]
  },
  {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
      "# \"Gate-based ns, 10⁻³\"\n",
      "results[2].diagram.space"
    ]
  },
  {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
      "# \"Gate-based ns, 10⁻⁴\"\n",
      "results[3].diagram.space"
    ]
  },
  {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
      "# \"Majorana ns, 10⁻⁴\"\n",
      "results[4].diagram.space"
    ]
  },
  {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
      " # \"Majorana ns, 10⁻⁶\"\n",
      "results[5].diagram.space"
    ]
  },
  {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
      "### Time chart\n",
      "We can also visualize the time required to execute the algorithm as it relates to each T factory invocation runtime and the number of T factory invocations.\n",
      "\n",
      "\n",
      "To show the time chart for a configuration from our experiment, use the following syntax:\n",
      "\n",
      "```\n",
      "    # Use the index of the desired configuration you want to visualize to get the results for that configuration.\n",
      "    \n",
      "    results[<item index>].diagram.time\n",
      "\n",
      "    # For example, this command will produce a chart showing the distribution of physical qubits for the Gate-based µs, 10⁻³ configuration set.\n",
      "    results[0].diagram.time\n",
      "```\n",
      "\n",
      "Below, let's visualize the space diagrams for each configuration set."
    ]
  },
  {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
      "> You must run the `results[<item index>].diagram.time` command in a separate code cell per label.\n",
      ">\n",
      "> You cannot visualize the time and space diagrams in the same cell.\n",
      ">\n",
      "> If you run an algorithm which only has one configured set of qubit parameters and one result set, specifying the label would not be necessary. You could simply run `result.diagram.time`"
    ]
  },
  {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
      "# Feel free to try out different configuration sets by changing the index. \n",
      "# This example produces the time diagram for the Gate-based µs, 10⁻³ configuration set.\n",
      "results[0].diagram.time"
    ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "From the results we can observe that a large fraction of physical qubits is used\n",
    "for the T factories.  To understand why, it's important to remark that the\n",
    "overall algorithm runtime is determined based on the number of logical\n",
    "operations (also called logical cycles or logical depth).  The runtime limits\n",
    "the number of invocations of a single T factory.  The total number of T factory\n",
    "copies is computed based on the total number of required T states divided by the\n",
    "number of possible invocations.  Therefore, if the algorithm would run longer, a\n",
    "T factory can be invoked more often, which may allow to compute all required T\n",
    "states with less T factory copies.\n",
    "\n",
    "Since T factory fraction is high, while at the same time the physical runtime is\n",
    "relatively small, this is a good opportunity for a space-time optimization based\n",
    "on the logical depth.  We can make an algorithm run longer, by inserting no-op\n",
    "(no operation or idle) operations.  We do this using the `logical_depth_factor`\n",
    "constraint. For example, a value of 2 means that the number of cycles should be\n",
    "twice as much, i.e., one no-op per operation; or, a value of 1.5 means that the\n",
    "number of cycles is 50% more, i.e., one no-op for every two operations.\n",
    "\n",
    "Please note that the algorithm runtime may increase by a larger factor than the\n",
    "`logical_depth_factor`.  This is because no-ops also can incur logical errors,\n",
    "and therefore the required logical error rate is lower, which in turn may\n",
    "increase the required code distance, therefore leading a to a longer execution\n",
    "time of a logical cycle.\n",
    "\n",
    "In the balanced implementation that is described in the paper, we increase the\n",
    "logical depth by a factor of 10.  We do this by updating the `params` variable.\n",
    "All other parameters remain unchanged."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "params.constraints.logical_depth_factor = 10\n",
    "\n",
    "job = estimator.submit(IsingModel2DSim, input_params=params)\n",
    "results_balanced = job.get_results()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We print the results for the balanced implementation as a summary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "results_balanced.summary_data_frame(labels=labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note how the T factory fraction is much smaller now and the number of total\n",
    "physical qubits decreased as a result.  The logical depth increased exactly by a\n",
    "factor of 10, whereas the runtime increased by a factor of at least 10, since in\n",
    "most cases the code distance is higher."
   ]
  },
  {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
      "To better understand how the balanced implementation changed the space distribution and runtime of the algorithm, try visualizing the new result set using the space and time diagrams discussed above."
    ]
  },
  {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
      "# Feel free to try out different configuration sets by changing the index. \n",
      "# This example produces the time diagram for the Gate-based µs, 10⁻³ configuration set.\n",
      "results_balanced[0].diagram.space"
    ]
  },
  {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
      "# Feel free to try out different configuration sets by changing the index. \n",
      "# This example produces the time diagram for the Gate-based µs, 10⁻³ configuration set.\n",
      "results_balanced[0].diagram.time"
    ]
  },
  {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
      "You could even compare the previous implementation with the balanced implementation by re-running the space and time diagrams with the original result set and comparing to the new diagrams."
    ]
  },
  {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
      "# Feel free to try out different configuration sets by changing the index. \n",
      "# This example produces the time diagram for the Gate-based µs, 10⁻³ configuration set.\n",
      "results[0].diagram.space"
    ]
  },
  {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
      "# Feel free to try out different configuration sets by changing the index. \n",
      "# This example produces the time diagram for the Gate-based µs, 10⁻³ configuration set.\n",
      "results[0].diagram.time"
    ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Next steps\n",
    "\n",
    "The numbers in the table match the numbers in the paper [Assessing requirements\n",
    "for scaling quantum computers to real-world impact](https://aka.ms/AQ/RE/Paper).\n",
    "Feel free to use this table as a starting point for your own experiments.  For\n",
    "example, you can\n",
    "\n",
    "* explore how the results change by modifying the operation arguments of the Ising\n",
    "  model instance\n",
    "* explore space- and time-trade-offs by changing the value for\n",
    "  `logical_depth_factor`\n",
    "* Visualize these trade-offs with the space and time diagrams\n",
    "* use other or customized qubit parameters"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
